﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using ComputerGraphics.Graphics;
using ComputerGraphics.Graphics;
using ComputerGraphics.Geometry;
using System.Text.RegularExpressions;

namespace ComputerGraphics.Application
{
    public static class SetXMLReader
    {
        private static Point3D Resolve3DPoint(String str)
        {
            Match m = Regex.Match(str, @"\((.*),(.*),(.*)\)");

            return new Point3D(Double.Parse(m.Groups[1].Value), Double.Parse(m.Groups[2].Value), Double.Parse(m.Groups[3].Value));
        }

        public static Camera ReadCameraFromXML(String filename)
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(filename);

            Camera camera = null;

            // Get camera info

            XmlNodeList xnl = doc.DocumentElement.SelectNodes("/Set/Camera[1]");
            foreach (XmlNode n in xnl)
            {
                camera = new Camera(Resolve3DPoint(n.Attributes["viewPoint"].InnerText), Resolve3DPoint(n.Attributes["viewPlaneCenter"].InnerText), Resolve3DPoint(n.Attributes["reference"].InnerText), Double.Parse(n.Attributes["viewAngle"].InnerText));
            }

            return camera;
        }

        public static Scene ReadSceneFromXML(String filename)
        {
            XmlDocument doc = new XmlDocument();
            XmlNodeList xnl;

            Scene scene = null;
            AmbientLight aml = null;
            List<PointLight> pll = new List<PointLight>();

            doc.Load(filename);

            // Get scene info
            xnl = doc.DocumentElement.SelectNodes("/Set/Scene[1]");
            String sbkgcol = xnl[0].Attributes["backgroundColor"].InnerText;

            // Get illumination
            xnl = doc.DocumentElement.SelectNodes("/Set/Scene[1]/Illumination[1]/*");
            foreach (XmlNode n in xnl)
            {
                switch (n.Name)
                {
                    case "AmbientLight":
                        aml = new AmbientLight(ResolveColorTriple(n.Attributes["color"].InnerText));
                        break;
                    case "PointLight":
                        pll.Add(new PointLight(Resolve3DPoint(n.Attributes["position"].InnerText), ResolveColorTriple(n.Attributes["color"].InnerText)));
                        break;
                }
            }
            scene = new Scene(ResolveColorTriple(sbkgcol), aml);
            foreach (PointLight pl in pll) scene.AddPointLight(pl);

            // Get shapes
            xnl = doc.DocumentElement.SelectNodes("/Set/Scene[1]/Shapes[1]/*");
            foreach (XmlNode n in xnl)
            {
                switch (n.Name)
                {
                    case "Sphere" : 
                        scene.AddShape(new Sphere(Resolve3DPoint(n.Attributes["center"].InnerText), Double.Parse(n.Attributes["radius"].InnerText), ResolveTexture(n)));
                        break;
                    case "Cube" :
                        scene.AddShape(new Cube(Resolve3DPoint(n.Attributes["origin"].InnerText), Resolve3DPoint(n.Attributes["x"].InnerText), Resolve3DPoint(n.Attributes["y"].InnerText), Resolve3DPoint(n.Attributes["z"].InnerText), ResolveTexture(n)));
                        break;
                    case "Tetrahedron":
                        scene.AddShape(new Tetrahedron(Resolve3DPoint(n.Attributes["origin"].InnerText), Resolve3DPoint(n.Attributes["x"].InnerText), Resolve3DPoint(n.Attributes["y"].InnerText), Resolve3DPoint(n.Attributes["z"].InnerText), ResolveTexture(n)));
                        break;
                    case "Cylinder" :
                        scene.AddShape(new Cylinder(Resolve3DPoint(n.Attributes["center"].InnerText), Resolve3DPoint(n.Attributes["n"].InnerText), Double.Parse(n.Attributes["radius"].InnerText), ResolveTexture(n)));
                        break;
                    case "Tube" :
                        scene.AddShape(new Tube(Resolve3DPoint(n.Attributes["center"].InnerText), Resolve3DPoint(n.Attributes["n"].InnerText), Double.Parse(n.Attributes["radius"].InnerText), ResolveTexture(n)));
                        break;
                    case "Plane" :
                        scene.AddShape(new Plane(Resolve3DPoint(n.Attributes["origin"].InnerText), Resolve3DPoint(n.Attributes["normal"].InnerText), ResolveTexture(n)));
                        break;
                    case "Parallelogram" :
                        scene.AddShape(new Parallelogram(Resolve3DPoint(n.Attributes["origin"].InnerText), Resolve3DPoint(n.Attributes["x"].InnerText), Resolve3DPoint(n.Attributes["y"].InnerText), ResolveTexture(n)));
                        break;
                    case "Circle" :
                        scene.AddShape(new Circle(Resolve3DPoint(n.Attributes["center"].InnerText), Resolve3DPoint(n.Attributes["normal"].InnerText), Double.Parse(n.Attributes["radius"].InnerText), ResolveTexture(n)));
                        break;
                    case "Triangle" :
                        scene.AddShape(new Triangle(Resolve3DPoint(n.Attributes["a"].InnerText), Resolve3DPoint(n.Attributes["b"].InnerText), Resolve3DPoint(n.Attributes["c"].InnerText), ResolveTexture(n)));
                        break;
                    case "Polygon" :
                        List<Point3D> vlist = new List<Point3D>();
                        XmlNodeList pxnl = n.SelectNodes("./Vertices/Vertex");
                        foreach (XmlNode pxn in pxnl)
                            vlist.Add(Resolve3DPoint(pxn.Attributes["position"].InnerText));
                        scene.AddShape(new Polygon(vlist, ResolveTexture(n)));
                        break;
                }
            }

            return scene;
        }

        private static Texture ResolveTexture(XmlNode n)
        {
            Texture ret = null;
            XmlNodeList xnl = n.SelectNodes("./*[1]");

            foreach (XmlNode tn in xnl)
            {
                ret = new Texture(ResolveColorTriple(tn.Attributes["diffuseColor"].InnerText), Double.Parse(tn.Attributes["ambientRflctCoef"].InnerText), 
                    Double.Parse(tn.Attributes["diffuseRflctCoef"].InnerText), Double.Parse(tn.Attributes["specularCoef"].InnerText), 
                    Double.Parse(tn.Attributes["transmissionCoef"].InnerText), Double.Parse(tn.Attributes["eta"].InnerText));
            }

            return ret;
        }

        private static ColorTriple ResolveColorTriple(string str)
        {
            Match m = Regex.Match(str, @"\((.*),(.*),(.*)\)");

            return new ColorTriple(Double.Parse(m.Groups[1].Value), Double.Parse(m.Groups[2].Value), Double.Parse(m.Groups[3].Value));
        }
    }
}
